﻿using Dotnet.Utils.Utility.AssemblyUtil;
using Dotnet.Utils.Utility.ObjectExtensions;
using System;
using System.Collections;
using System.Linq;
using System.Reflection;
using System.Xml.Linq;
using System.Xml.Serialization;

namespace Dotnet.Utils.Utility.Xml
{
    public class XSDUtil
    {
        public static void XsdGenerate<T>(string path)
        {
            Type typeFromHandle = typeof(T);
            XNamespace ns = "http://www.w3.org/2001/XMLSchema";
            XDocument xdocument = new XDocument(new XDeclaration("1.0", "utf-8", "yes"), new object[]
            {
                new XElement(ns + "schema", new XAttribute(XNamespace.Xmlns + "xs", "http://www.w3.org/2001/XMLSchema"))
            });
            CreateComplexType(typeFromHandle, xdocument.Root, xdocument.Root, null, false);
            xdocument.Save(path);
        }

        private static void CreateComplexType(Type t, XElement root, XElement parent, string elementName = null, bool allowMulty = false)
        {
            XNamespace namespaceOfPrefix = root.GetNamespaceOfPrefix("xs");
            XElement xelement = new XElement(namespaceOfPrefix + "element", new XAttribute("name", string.IsNullOrEmpty(elementName) ? t.Name : elementName));
            if (allowMulty)
            {
                xelement.SetAttributeValue("minOccurs", "0");
                xelement.SetAttributeValue("maxOccurs", "unbounded");
            }
            XElement xelement2 = new XElement(namespaceOfPrefix + "complexType");
            XElement xelement3 = new XElement(namespaceOfPrefix + "sequence");
            foreach (PropertyInfo propertyInfo in t.GetProperties())
            {
                string localName = "element";
                string value = propertyInfo.Name;
                XmlAttributeAttribute attribute = AttributeUtil.GetAttribute<XmlAttributeAttribute>(propertyInfo);
                if (attribute != null)
                {
                    localName = "attribute";
                    value = attribute.AttributeName;
                }
                else
                {
                    XmlElementAttribute attribute2 = AttributeUtil.GetAttribute<XmlElementAttribute>(propertyInfo);
                    if (attribute2 != null)
                    {
                        value = attribute2.ElementName;
                    }
                }
                Type propertyType = propertyInfo.PropertyType;
                string fullName = propertyType.FullName;
                if (!GeneralType.Contains(fullName))
                {
                    XElement content = new XElement(namespaceOfPrefix + localName, new XAttribute("name", value));
                    if (propertyType.IsEnum)
                    {
                        XElement xelement4 = new XElement(namespaceOfPrefix + "simpleType", new XAttribute("name", propertyType.Name));
                        XElement xelement5 = new XElement(namespaceOfPrefix + "restriction", new XAttribute("base", "xs:string"));
                        foreach (string value2 in Enum.GetNames(propertyType))
                        {
                            XElement content2 = new XElement(namespaceOfPrefix + "enumeration", new XAttribute("value", value2));
                            xelement5.Add(content2);
                        }
                        xelement4.Add(xelement5);
                        root.Add(xelement4);
                    }
                    else if (propertyType.GetInterface(typeof(IList).FullName) != null && propertyType.IsGenericType)
                    {
                        Type type = propertyType.GetGenericArguments()[0];
                        XmlArrayAttribute attribute3 = AttributeUtil.GetAttribute<XmlArrayAttribute>(propertyInfo);
                        if (attribute3 != null)
                        {
                            value = attribute3.ElementName;
                        }
                        string elementName2 = type.Name;
                        XmlArrayItemAttribute attribute4 = AttributeUtil.GetAttribute<XmlArrayItemAttribute>(propertyInfo);
                        if (attribute4 != null)
                        {
                            elementName2 = attribute4.ElementName;
                        }
                        XElement xelement6 = new XElement(namespaceOfPrefix + "sequence");
                        CreateComplexType(type, root, xelement6, elementName2, true);
                        content = new XElement(namespaceOfPrefix + localName, new object[]
                        {
                            new XAttribute("name", value),
                            new XElement(namespaceOfPrefix + "complexType", xelement6)
                        });
                    }
                    else if (propertyType.IsClass || propertyType.IsValueType)
                    {
                    }
                    if (attribute != null)
                    {
                        xelement2.Add(new XElement(namespaceOfPrefix + localName, new object[]
                        {
                            new XAttribute("name", value),
                            new XAttribute("type", propertyType.Name)
                        }));
                    }
                    else
                    {
                        xelement3.Add(content);
                    }
                }
                else if (attribute != null)
                {
                    xelement2.Add(new XElement(namespaceOfPrefix + localName, new object[]
                    {
                        new XAttribute("name", value),
                        new XAttribute("type", GeneralType.ConvertXSDType(propertyType.FullName))
                    }));
                }
                else
                {
                    xelement3.Add(new XElement(namespaceOfPrefix + localName, new object[]
                    {
                        new XAttribute("name", value),
                        new XAttribute("type", GeneralType.ConvertXSDType(propertyType.FullName)),
                        new XAttribute("minOccurs", "0"),
                        new XAttribute("maxOccurs", "1")
                    }));
                }
            }
            if (xelement3.Nodes().Count() > 0)
            {
                xelement2.Add(xelement3);
            }
            xelement.Add(xelement2);
            parent.Add(xelement);
        }
    }
}